package similarity

import (
	"hash"
	"hash/fnv"
	"math"
	"strings"
	"sync"
)

type simHash struct {
	pool sync.Pool
}

func newSimHash() *simHash {
	s := new(simHash)
	s.pool = sync.Pool{
		New: func() interface{} {
			return fnv.New64a()
		},
	}
	return s
}

// cycleHash 计算给定词语切片的SimHash值
// 该算法通过统计词语的哈希值中各bit位的加权和来生成最终的SimHash值
// 参数:
//
//	words []string - 需要计算SimHash值的词语切片
//
// 返回值:
//
//	uint64 - 计算得到的64位SimHash值
func (s *simHash) cycleHash(words []string) uint64 {
	// 1. 为每个词计算特征向量
	features := make([]int, 64) // 64位特征向量
	for _, word := range words {
		if len(strings.TrimSpace(word)) == 0 {
			continue
		}

		// 计算词的哈希值
		_hash := s.pool.Get().(hash.Hash64)
		_, _ = _hash.Write([]byte(word))
		hashValue := _hash.Sum64()
		_hash.Reset()
		s.pool.Put(_hash)

		// 将哈希值转换为64位的二进制表示，并更新特征向量
		for i := 0; i < 64; i++ {
			if (hashValue & (1 << uint(i))) != 0 {
				features[i]++
			} else {
				features[i]--
			}
		}
	}

	// 2. 根据特征向量生成SimHash值
	var simhash uint64
	for i := 0; i < 64; i++ {
		if features[i] > 0 {
			simhash |= 1 << uint(i)
		}
	}
	return simhash
}

// HammingDistance 计算两个SimHash值之间的汉明距离
func (s *simHash) hammingDistance(hash1, hash2 uint64) int {
	// 异或运算，相同位为0，不同位为1
	xor := hash1 ^ hash2

	// 计算异或结果中1的个数
	distance := 0
	for xor != 0 {
		distance++
		xor &= xor - 1 // 清除最右边的1
	}

	return distance
}

// SimHashSimilarity 计算两个文本的相似度（0-1之间的浮点数）
func (s *simHash) SimHashSimilarity(text1, text2 []string) float64 {

	distance := s.hammingDistance(s.cycleHash(text1), s.cycleHash(text2))

	// 相似度 = 1 - 汉明距离/总位数
	similarity := 1.0 - float64(distance)/64.0

	return math.Max(0, similarity) // 确保相似度不小于0
}
